/// <reference name="MicrosoftAjax.debug.js" />
/// <reference path="../../Resources/js/jquery/jquery.debug.js" />
/// <reference path="../Root.js" />
/// <reference path="../Additions/Addition.debug.js" />
/// <reference path="../Additions/Constraint.debug.js" />

function MItem(node, basis) {
    /// <field name="node" domElement="true">
    /// The node that the item is bound to.
    /// </field>
    /// <field name="basis" type="MBasis">
    /// The basis of the item
    /// </field>
    MItem.initializeBase(this, []);
    this._markers = new Object();
    this.node = node;
    this.mode = this.node.getAttribute('mode') || MItem.EditMode;
    $(this.node).click(Function.createDelegate(this, this.itemClicked));
    this.descriptor = null;
    if (basis) {
        this.basis = basis;
    }
    else {
        this.basis = MBasis.getById(node.id);
    }
    this._copyEventsFromBasis(['Changed', 'DescriptionChanged', 'Focused', 'Loaded', 'Loading', 'ValidationError', 'ValidationSuccess', 'Unloaded', 'Unloading', 'UsageChanged']);
    this.createAdditions();
}

MItem.Element = "MForm-El";
MItem.Optional = "MForm-Op";
MItem.Required = "MForm-Rq";
MItem.Leaf = "MForm-Lf";
MItem.ChoiceContainer = "MForm-ChC";
MItem.ButtonContainer = "MForm-BtnC";

MItem.ViewMode = "View";
MItem.EditMode = "Edit";

MItem.prototype = {
    addChanged: function (handler) {
        /// <summary>
        /// Add an event handler for the Changed event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />        
        this.get_events().addHandler('Changed', handler);
    },

    removeChanged: function (handler) {
        /// <summary>
        /// Remove an event handler from the Changed event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('Changed', handler);
    },

    raiseChanged: function (eventArgs) {
        /// <summary>
        /// Raise the Changed event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the Changed event
        /// </param>
        /// <returns />
        var handler = this.get_events().getHandler('Changed');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addDescriptionChanged: function (handler) {
        /// <summary>
        /// Add an event handler for the DescriptionChanged event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('DescriptionChanged', handler);
    },

    removeDescriptionChanged: function (handler) {
        /// <summary>
        /// Remove an event handler from the DescriptionChanged event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('DescriptionChanged', handler);
    },

    raiseDescriptionChanged: function (eventArgs) {
        /// <summary>
        /// Raise the DescriptionChanged event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the DescriptionChanged event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('DescriptionChanged');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addFocused: function (handler) {
        /// <summary>
        /// Add an event handler for the Focused event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />        
        this.get_events().addHandler('Focused', handler);
    },

    removeFocused: function (handler) {
        /// <summary>
        /// Remove an event handler from the Focused event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('Focused', handler);
    },

    raiseFocused: function (eventArgs) {
        /// <summary>
        /// Raise the Focused event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the Focused event
        /// </param>
        /// <returns />
        var handler = this.get_events().getHandler('Focused');
        if (handler) {
            handler(this, eventArgs);
        }
        var parentItem = this.getParentItem();
        if (parentItem) {
            parentItem.raiseFocused();
        }
    },

    addLoaded: function (handler) {
        /// <summary>
        /// Add an event handler for the Loaded event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('Loaded', handler);
    },

    removeLoaded: function (handler) {
        /// <summary>
        /// Remove an event handler from the Loaded event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('Loaded', handler);
    },

    raiseLoaded: function (eventArgs) {
        /// <summary>
        /// Raise the Loaded event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the Loaded event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('Loaded');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addLoading: function (handler) {
        /// <summary>
        /// Add an event handler for the Loading event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('Loading', handler);
    },

    removeLoading: function (handler) {
        /// <summary>
        /// Remove an event handler from the Loading event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('Loading', handler);
    },

    raiseLoading: function (eventArgs) {
        /// <summary>
        /// Raise the Loading event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the Loading event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('Loading');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addValidationError: function (handler) {
        /// <summary>
        /// Add an event handler for the ValidationError event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('ValidationError', handler);
    },

    removeValidationError: function (handler) {
        /// <summary>
        /// Remove an event handler from the ValidationError event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('ValidationError', handler);
    },

    raiseValidationError: function (eventArgs) {
        /// <summary>
        /// Raise the ValidationError event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the ValidationError event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('ValidationError');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addValidationSuccess: function (handler) {
        /// <summary>
        /// Add an event handler for the ValidationSuccess event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('ValidationSuccess', handler);
    },

    removeValidationSuccess: function (handler) {
        /// <summary>
        /// Remove an event handler from the ValidationSuccess event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('ValidationSuccess', handler);
    },

    raiseValidationSuccess: function (eventArgs) {
        /// <summary>
        /// Raise the ValidationSuccess event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the ValidationSuccess event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('ValidationSuccess');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addUnloaded: function (handler) {
        /// <summary>
        /// Add an event handler for the Unloaded event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('Unloaded', handler);
    },

    removeUnloaded: function (handler) {
        /// <summary>
        /// Remove an event handler from the Unloaded event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('Unloaded', handler);
    },

    raiseUnloaded: function (eventArgs) {
        /// <summary>
        /// Raise the Unloaded event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the Unloaded event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('Unloaded');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addUnloading: function (handler) {
        /// <summary>
        /// Add an event handler for the Unloading event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('Unloading', handler);
    },

    removeUnloading: function (handler) {
        /// <summary>
        /// Remove an event handler from the Unloading event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('Unloading', handler);
    },

    raiseUnloading: function (eventArgs) {
        /// <summary>
        /// Raise the Unloading event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the Unloading event
        /// </param>
        /// <returns />

        var handler = this.get_events().getHandler('Unloading');
        if (handler) {
            handler(this, eventArgs);
        }
    },

    addUsageChanged: function (handler) {
        /// <summary>
        /// Add an event handler for the UsageChanged event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />        
        this.get_events().addHandler('UsageChanged', handler);
    },

    removeUsageChanged: function (handler) {
        /// <summary>
        /// Remove an event handler from the UsageChanged event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('UsageChanged', handler);
    },

    raiseUsageChanged: function (eventArgs) {
        /// <summary>
        /// Raise the UsageChanged event
        /// </summary>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="false">
        /// Event arguments for the UsageChanged event
        /// </param>
        /// <returns />
        var handler = this.get_events().getHandler('UsageChanged');
        if (handler) {
            handler(this, eventArgs);
        }
        var children = this.getChildren();
        for (var i = 0; i < children.length; i++) {
            children[i].raiseUsageChanged(eventArgs);
        }
    },

    itemClicked: function (clickEvent) {
        /// <summary>
        /// shows appropriate buttons for the item
        /// </summary>
        clickEvent.stopPropagation();
        this.click();
    },

    click: function () {
        if (BM.AddButtons == null) return;
        var addButtons = BM.AddButtons.get();
        if (addButtons == null) return;
        addButtons.update(this);
    },

    _copyEventsFromBasis: function (eventNames) {
        for (var i = 0; i < eventNames.length; i++) {
            this._copyEventFromBasis(eventNames[i]);
        }
    },

    _copyEventFromBasis: function (eventName) {
        var handler = this.basis.get_events().getHandler(eventName);
        if (handler) {
            this.get_events().addHandler(eventName, handler);
        }
    },

    getDescriptor: function () {
        /// <summary>
        /// returns an object responsible for descriptions
        /// </summary>
        /// <returns type="BM.Descriptor" />
        if (this.descriptor == null) {
            this.descriptor = new BM.DefaultDescriptor(this);
        }
        return this.descriptor;
    },

    getDescription: function () {
        /// <summary>
        /// returns the output of the descriptor object
        /// </summary>
        /// <returns type="String" />
        return this.getDescriptor().getDescription();
    },

    load: function () {
        /// <summary>
        /// fires all the attached loading handlers and then does the same with its children
        /// </summary>        
        this.raiseLoading();
        var children = this.getChildren();
        for (var i = 0; i < children.length; i++) {
            children[i].load();
        }

        this.raiseLoaded();
    },

    unload: function () {
        /// <summary>
        /// fires all the attached unloading handlers and then does the same with its children
        /// </summary>            
        this.raiseUnloading();
        var children = this.getChildren();
        for (var i = 0; i < children.length; i++) {
            children[i].unload();
        }
    },

    dispose: function () {
        MItem.callBaseMethod(this, 'dispose');
        this.basis = null;
        this.descriptor = null;
        this.node = null;
    },

    isInUse: function () {
        /// <returns type="Boolean >
        /// returns information if the current item is in use 
        /// (it is not in use if i.e. it is one of the choices that is not selected)
        /// </returns>
        var parentItem = this.getParentItem();

        if (parentItem == null) {
            return true;
        }

        if (ChoiceItem.isInstanceOfType(parentItem)) {
            var parentSelectedItems = parentItem.getSelectedItems();
            if (parentSelectedItems == null) return false;
            if (!Array.contains(parentSelectedItems, this)) return false;
        }

        return parentItem.isInUse();
    },

    getMode: function () {
        /// <returns type="String" >
        /// the MItem mode, can be Edit or View
        /// </returns>
        return this.mode;
    },

    getMForm: function () {
        /// <summary>
        /// returns the root MForm object
        /// </summary>    
        /// <returns type="MForm" >
        /// the root MForm object
        /// </returns>
        return this.basis.getMForm();
    },

    getNode: function () {
        /// <summary>
        /// returns the node associated with the item
        /// </summary>    
        /// <returns domElement="true" >
        /// the associated node
        /// </returns>        
        return this.node;
    },

    getParentItem: function () {
        /// <returns type="MItem" >
        /// returns the parent item
        /// </returns>
        var parentBasis = this.basis.parent;
        if (parentBasis == null) return null;
        return parentBasis.getAncestorInstance(this.getId());
    },

    getChildren: function () {
        /// <summary>
        /// returns all item children of the current item
        /// </summary>
        /// <returns type="Array" elementType="MItem" >
        /// the children
        /// </returns>
        return this._getChildrenFromBases(this.basis.getChildren());
    },

    getChildrenByName: function (qualifiedName) {
        /// <summary>
        /// returns children that match the specified name
        /// </summary>
        /// <param name="qualifiedName" type="String" mayBeNull="false">
        /// the qualified name of the children
        /// </param>
        /// <returns type="Array" elementType="MItem" >
        /// the children
        /// </returns>
        return this._getChildrenFromBases([this.basis.getChild(qualifiedName)]);
    },

    _getChildrenFromBases: function (bases) {
        var items = new Array();
        for (var i = 0; i < bases.length; i++) {
            if (bases[i] == null) {
                continue;
            }

            items = items.concat(bases[i].getDescendantInstances(this.getId()));
        }

        return items;
    },

    getChildByName: function (name, index) {
        /// <summary>
        /// returns a child that matches the name and index
        /// </summary>
        /// <param name="name" type="String" mayBeNull="false">
        /// the name of the child
        /// </param>
        /// <param name="index" type="Number" mayBeNull="true">
        /// the index of the child
        /// </param>
        /// <returns type="MItem" mayBeNull="true" >
        /// the child
        /// </returns>
        index = index || 1;
        var children = this.getChildrenByName(name);
        if (children.length == 0) return null;
        for (var i = 0; i < children.length; i++) {
            if (children[i].getIndex() == index) {
                return children[i];
            }
        }

        return null;
    },

    getButtonContainer: function () {
        /// <returns type="String" >
        /// the container node of buttons
        /// </returns>
        return $(this.node).children("." + MItem.ButtonContainer)[0];
    },

    getButton: function () {
        try {
            return Button.get(this);
        }
        catch (err) {
            return null;
        }
    },

    getChildrenButtons: function (qualifiedName) {
        /// <param name="qualifiedName" type="String" mayBeNull="true">
        /// if other than null, filters the children nodes to return only the one that reflects adding/setting a child item of name ...
        /// </param>
        /// <returns type="jQuery">
        /// the jQuery array of buttons that are displayed for this item
        /// </returns>
        var bc = this.getButtonContainer();
        if (!bc) return $([]);
        var result = $(bc).children(".Add");
        if (qualifiedName != null) {
            result = result.filter(Button.getButtonNodeQuery(this.getId(), qualifiedName));
        }
        return result;
    },

    focus: function () {
        /// <summary>
        /// sets focus on the first possible element inside
        /// </summary>
        /// <returns type="Boolean" >
        /// information if setting the focus was successful
        /// </returns>
        var children = this.getChildren();
        if (children.length == 0) {
            this.click();
            return;
        }
        for (var i = 0; i < children.length; i++) {
            if (children[i].focus()) return true;
        }
    },

    getIndex: function () {
        /// <returns type="Number" integer="true" >
        /// the index of the current item
        /// </returns>
        var id = this.getId();
        var underscoreIndex = id.lastIndexOf("_");
        return parseInt(id.substring(underscoreIndex + 1));
    },

    getSiblings: function () {
        /// <summary>
        /// returns all siblings of the item, with the item itself
        /// </summary>
        /// <returns type="Array" elementType="MItem" >
        /// the children
        /// </returns>
        return this.getParentItem().getChildrenByName(this.getQualifiedName());
    },

    getSiblingId: function (index) {
        /// <param name="index" type="Number" integer="true" mayBeNull="true" >
        /// the index of the sibling
        /// </param>
        /// <returns type="String" >
        /// the id of the sibling with the specified index, or if no index, the id of the first sibling
        /// </returns>
        return Path.getSiblingId(this.getId(), index);
    },

    getId: function () {
        /// <returns type="String" >
        /// the id of the item
        /// </returns>
        return this.node.id;
    },

    getName: function () {
        /// <returns type="String" >
        /// the descriptive name of the item
        /// </returns>
        return this.node.getAttribute('name');
    },

    getQualifiedName: function () {
        /// <returns type="String" >
        /// the string representation of the qualified name
        /// </returns>
        return this.basis.get_qualifiedName();
    },

    getMaxOccurs: function () {
        /// <returns type="Number" integer="true" >
        /// the maximal occurs of the items' siblings
        /// </returns>
        return this.basis.maxOccurs;
    },

    getMinOccurs: function () {
        /// <returns type="Number" integer="true" >
        /// the minimal occurs of the items' siblings
        /// </returns>
        return this.basis.minOccurs;
    },

    setRequired: function () {
        /// <summary>
        /// changes the css class of the node to denote it is required
        /// </summary>
        /// <returns />
        $(this.node).removeClass(MItem.Optional).addClass(MItem.Required);
    },

    setOptional: function () {
        /// <summary>
        /// changes the css class of the node to denote it is optional
        /// </summary>
        /// <returns />
        $(this.node).removeClass(MItem.Required).addClass(MItem.Optional);
    },

    isOptional: function () {
        /// <summary>
        /// checks if the item is optional
        /// </summary>
        /// <returns />
        return $(this.node).hasClass(MItem.Optional);
    },

    getChildMarker: function (childName) {
        if (!this._markers[childName]) {
            this._markers[childName] = this.findChildMarkerIn($(this.getNode()), childName);
        }

        return this._markers[childName];
    },

    findChildMarkerIn: function (query, childName) {
        var childMarker = query.contents().filter(function () { return this.nodeType == 8 && this.nodeValue == childName })[0];
        if (childMarker != null) {
            return childMarker;
        }

        return this.findChildMarkerIn(query.children(), childName);
    },

    show: function (speed) {
        /// <summary>
        /// shows the corresponding node
        /// </summary>
        /// <param name="speed" type="String" mayBeNull="true">
        /// the speed of showing
        /// </param>
        $(this.node).show(speed);
    },

    hide: function (speed) {
        /// <summary>
        /// hides the corresponding node
        /// </summary>
        /// <param name="speed" type="String" mayBeNull="true">
        /// the speed of hiding
        /// </param>
        $(this.node).hide(speed);
    },

    remove: function () {
        if (this.getSiblings().length <= this.getMinOccurs()) {
            alert(BM.Messages['CannotRemove_MinOccurs'] + ' ' + this.getMinOccurs());
            return false;
        }
        this.unload();
        this.basis.unregister(this);
        var button = this.getButton();
        if (button) button.refresh();
        this.removeNode();
        return true;
    },

    removeNode: function () {
        var input = document.createElement('input');
        input.setAttribute('type', 'hidden');
        input.setAttribute('name', this.getId() + '_deleted');
        input.setAttribute('id', this.getId() + '_deleted');
        input.setAttribute('value', 'deleted');
        this.getMForm().get_element().appendChild(input);
        if (this.getNode() && this.getNode().parentNode) {
            this.getNode().parentNode.removeChild(this.getNode());
        }
    },

    add: function (qualifiedName, silentMode) {
        var item = this.create(qualifiedName);
        if (item == null) return false;
        item.load();
        var button = item.getButton();
        if (button) button.refresh();
        if (!silentMode) {
            item.focus();
        }
        return item;
    },

    create: function (qualifiedName) {
        var basis = this.basis.getChild(qualifiedName);
        if (!basis) return;
        if (basis.get_mode() == MItem.ViewMode) {
            return;
        }
        if (basis.maxOccurs <= basis.getDescendantInstances(this.getId()).length) {
            alert(BM.Messages['CannotAdd_MaxOccurs'] + ' ' + basis.maxOccurs + '; ' + qualifiedName);
            return;
        }

        var newElem = MItem.cloneNode(this, basis);
        var marker = this.getChildMarker(qualifiedName);
        marker.parentNode.insertBefore(newElem, marker);
        basis.incrementAvailableIndex(this);
        basis.register(newElem);
        return MItem.getByNode(newElem);
    },

    getAdditionDatas: function () {
        var additionDatasComment = $(this.node).contents().filter(function () { return this.nodeType === 8 && this.nodeValue.startsWith('[') })[0];
        if (additionDatasComment == null) {
            return null;
        }

        return Sys.Serialization.JavaScriptSerializer.deserialize(additionDatasComment.nodeValue);
    },

    createAdditions: function () {
        this.additions = new Array();
        this.constraints = new Array();
        var additionDatas = this.getAdditionDatas();
        if (!additionDatas) return;
        for (var i = 0; i < additionDatas.length; i++) {
            if (!additionDatas[i]) {
                continue;
            }

            this._createAdditionFromData(additionDatas[i]);
        }
    },

    _createAdditionFromData: function (additionData) {
        if (additionData.cls) {
            var additionClass = eval(additionData.cls);
            var addition = new additionClass(this, additionData);
            this.addAddition(addition);
        }
    },

    addAddition: function (addition) {
        /// <summary>
        /// adds a specific addition to the array of additions
        /// </summary>
        /// <param name="addition" type="BM.Addition" mayBeNull="false">
        /// the addition object
        /// </param>
        this.additions.push(addition);
        if (addition.constructor.inheritsFrom(BM.Constraint)) {
            this.constraints.push(addition);
        }
    },

    getConstraints: function () {
        /// <returns type="Array" elementType="BM.Constraint" >
        /// returns all the constraints from the additions
        /// </returns>
        return this.constraints;
    },

    getAdditions: function () {
        return this.additions;
    },

    getConstraintValue: function (type) {
        for (var i = 0; i < this.constraints; i++) {
            if (this.constraints[i].getConstraintType() == type) {
                return this.constraints[i].getRestriction();
            }
        }
        return null;
    },

    getValue: function () {
        return null;
    },

    getInput: function () {
        return null;
    },

    isDisabled: function () {
        var children = this.getChildren()
        for (var i = 0; i < children.length; i++) {
            if (!children[i].isDisabled()) return false;
        }
        return true;
    },

    setDisabled: function () {
        var children = this.getChildren()
        for (var i = 0; i < children.length; i++) {
            children[i].setDisabled();
        }
    },

    setEnabled: function () {
        var children = this.getChildren()
        for (var i = 0; i < children.length; i++) {
            children[i].setEnabled();
        }
    },

    validate: function () {
        /// <returns type="Boolean >
        /// returns information if the item and its children are valid.
        /// </returns>
        if (this.isOptional()) {
            return true;
        }

        var children = this.getChildren();
        var isValid = this.validateSelf();
        for (var i = 0; i < children.length; i++) {
            if (!children[i].validate()) {
                isValid = false;
            }
        }
        return isValid;
    },

    validateSelf: function () {
        /// <returns type="Boolean >
        /// returns information if the item constraints are met.        
        /// </returns>
        for (var i = 0; i < this.getConstraints().length; i++) {
            var constraint = this.getConstraints()[i];
            if (!constraint.check()) {
                this.raiseValidationError([new BM.ConstraintValidationError(this, constraint)]);
                return false;
            }
        }
        this.raiseValidationSuccess([]);
        return true;
    }
}

MItem.registerClass("MItem", Sys.Component);

MItem.cloneNode = function(parentItem, basis) {
    var index = basis.getAvailableIndex(parentItem);
    var id = parentItem.getId() + '_' + basis.get_lastPart() + '_' + index;
    var idSeparator = parentItem.getMForm().get_idSeparator();
    var elem = document.createElement(basis.node.tagName);
    for (var i = 0; i < basis.node.attributes.length; i++) {
        var value = basis.node.attributes[i].nodeValue;
        if (value != null && typeof (value) == 'string') {
            elem.setAttribute(basis.node.attributes[i].name, value);
        }
    }
    elem.setAttribute('id', id);
    elem.className = elem.getAttribute('class');

    var modifiedText = $(basis.node).html().split(basis.node.id).join(elem.id);

    var uniqueBasisId = basis.getUniqueId(basis.node.id, idSeparator);
    var uniqueElementId = basis.getUniqueId(elem.id, idSeparator);    
    $(elem).html(modifiedText.split(uniqueBasisId).join(uniqueElementId));
    return elem;
}

MItem.remove = function(node) {
    ///	<summary>        
    /// removes the MItem in which the specified node is
    ///	</summary>
    if (!node) return;
    if ($(node).hasClass(MItem.Element)) {
        return MItem.getByNode(node).remove();
    }
    if (node.parentNode) {
        return MItem.remove(node.parentNode);
    }
}

MItem.create = function(node, basis) {
    ///	<summary>        
    /// based on the node type returns an appropriate MItem type
    ///	</summary>
    /// <param name="node" domElement="true" mayBeNull="false">
    /// the node to which MItem will be bound
    /// </param>
    /// <param name="basis" type="MBasis" mayBeNull="false">
    /// the item's basis
    /// </param>
    ///	<returns type="MItem" />
    var jqNode = $(node);
    if (jqNode.hasClass(MItem.Leaf)) {
        return new LeafItem(node, basis);
    }
    if (jqNode.hasClass(MItem.ChoiceContainer)) {
        return new ChoiceItem(node, basis);
    }
    return new MItem(node, basis);
}

MItem.getByNode = function(node) {
    ///	<summary>        
    ///		finds the item by bound element
    ///	</summary>
    /// <param name="node" domElement="true" mayBeNull="false">
    /// the node to which MItem is bound
    /// </param>
    ///	<returns type="MItem" />      
    return MItem.getById(node.id);
}

MItem.getById = function(id) {
    ///	<summary>        
    ///	finds the item by bound element's id attribute
    ///	</summary>
    /// <param name="id" type="String" mayBeNull="false">
    /// the id attribute of the node to which MItem is bound
    /// </param>
    ///	<returns type="MItem" />      
    if (!id) return null;
    var basis = MBasis.getById(id);
    return basis ? basis.getInstance(id) : null;
}